package hied.learning;

import hied.Example;
import hied.ExampleList;
import hied.Knowledge;
import hied.SimilarityCalculator;
import hied.WordPositions;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

public class Learning {

	private Map<String, WordPositionsCounter> wordsPositionsOccurences = new HashMap<String, WordPositionsCounter>();
	private Map<String, Map<String, Integer>> wordsCorrectFormsPossibilities = new HashMap<String, Map<String, Integer>>();
	private static final Pattern TOKENIZER = Pattern.compile("\\s+");
	private Knowledge knowledge;
	private static final SimilarityCalculator SIMILARITY_CALCULATOR = new SimilarityCalculator();

	public Learning(ExampleList examplesList) {
		String[] from, to;
		List<Example> examples = examplesList.getExamples();
		for (Example example : examples) {
			from = this.tokenize(example.getBefore());
			to = this.tokenize(example.getAfter());
			this.fillCorrectForms(from, to);
			this.fillPositions(to);
		}
		Map<String, WordPositions> wordPositions = this.generatePositionsStats();
		Map<String, String> correctForms = this.generateCorrectForms();
		this.knowledge = new Knowledge();
		this.knowledge.setWordsPositions(wordPositions);
		this.knowledge.setWordsCorrectForms(correctForms);
	}

	private Map<String, String> generateCorrectForms() {
		Map<String, String> correctForms = new HashMap<String, String>();
		for (String word : this.wordsCorrectFormsPossibilities.keySet()) {
			Map<String, Integer> map = wordsCorrectFormsPossibilities.get(word);
			String correctForm = this.findBestWord(map, word);
			correctForms.put(word, correctForm);
		}
		Set<String> words = new HashSet<String>(correctForms.values());
		for (String word : words) {
			if (correctForms.get(word) == null) {
				correctForms.put(word, word);
			}
		}
		return correctForms;
	}

	private String findBestWord(Map<String, Integer> map, String word) {
		double val = -Double.MAX_VALUE;
		String bestWord = "";
		double similarity;
		int occurences;
		for (String candidate : map.keySet()) {
			occurences = map.get(candidate);
			similarity = SIMILARITY_CALCULATOR.match(word, candidate);
			similarity *= occurences;
			if (similarity > val) {
				val = similarity;
				bestWord = candidate;
			}
		}
		return bestWord;
	}

	private Map<String, WordPositions> generatePositionsStats() {
		Map<String, WordPositions> wordsPositionsStats = new HashMap<String, WordPositions>();
		for (String word : wordsPositionsOccurences.keySet()) {
			WordPositionsCounter wpc = this.wordsPositionsOccurences.get(word);
			// System.out.println(word + " " + wpc);
			WordPositions wp = wpc.getWordPositions();
			// System.out.println(word + " " + wp);
			// System.out.println(wp.getBestPosition(3));
			wordsPositionsStats.put(word, wp);
		}
		return wordsPositionsStats;
	}

	private void fillPositions(String[] to) {
		WordPositionsCounter wp;
		for (int i = 0; i < to.length; i++) {
			wp = this.getWordPositionsCounter(to[i]);
			wp.addPosition(i, to.length);
		}
	}

	private WordPositionsCounter getWordPositionsCounter(String word) {
		WordPositionsCounter wp = this.wordsPositionsOccurences.get(word);
		if (wp == null) {
			wp = new WordPositionsCounter();
			this.wordsPositionsOccurences.put(word, wp);
		}
		return wp;
	}

	private void fillCorrectForms(String[] from, String[] to) {
		for (String f : from) {
			for (String t : to) {
				this.addPossibleCorrectForm(f, t);
			}
		}
	}

	private void addPossibleCorrectForm(String from, String to) {
		Map<String, Integer> map = this.getCorrectFormsMap(from);
		Integer count = map.get(to);
		if (count == null) {
			map.put(to, 1);
		} else {
			map.put(to, count + 1);
		}
	}

	private Map<String, Integer> getCorrectFormsMap(String from) {
		Map<String, Integer> map = this.wordsCorrectFormsPossibilities.get(from);
		if (map == null) {
			map = new HashMap<String, Integer>();
			this.wordsCorrectFormsPossibilities.put(from, map);
		}
		return map;
	}

	private String[] tokenize(String txt) {
		return TOKENIZER.split(txt);
	}

	public Knowledge getKnowledge() {
		return this.knowledge;
	}
}
